﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RWLib
{
    public class Timeline
    {
        public class Node
        {
            public List<Action> actions = new List<Action>();
            public List<Node> Nodes = new List<Node>();
            public List<Fluent> Fluents;
            public int t = 0;

            public Node() { }

            public Node(List<Fluent> fluents)
            {
                Fluents = new List<Fluent>();
                fluents.CopyTo(Fluents);
            }
        }

        private Node root;

        private void Build(Node node, List<List<Action>> actionQueue)
        {
            if (actionQueue == null || actionQueue.Count == 0) return;
            var toprocess = actionQueue.Skip(node.t).ToList();
            var processActions = toprocess.Any() ? toprocess.First() : new List<Action>();

            node.Nodes = new List<Node>();

            node.actions.Affect(node.Fluents);

            if (!processActions.Any()) return;
            Resolve(node, node.Fluents, processActions, node.t + 1);

            foreach (var n in node.Nodes)
            {
                Build(n, actionQueue);
            }
        }

        private void Resolve(Node node, List<Fluent> fluents, List<Action> list, int t)
        {
            var nodes = new List<Node>();

            nodes.AddRange(fluents.Permutate().Select(permutation => new Node { t = t, Fluents = permutation.Copy() }));

            var possibilities = fluents.Permutate();

            foreach (var action in list)
            {
                if (possibilities.All(n => !action.IsExecutable(n))) continue;
                var i = 0;
                while (i < nodes.Count() && (!action.IsExecutable(nodes[i].Fluents) || nodes[i].actions.Any(n => n.IsConflictingWith(action))))
                {
                    i++;
                }
                if (i < nodes.Count())
                {
                    nodes[i].actions.Add(action);
                }
                else
                {
                    for (var j = 0; j < possibilities.Count(); j++)
                    {
                        var possibility = possibilities[j].Copy();
                        if (action.IsExecutable(possibility))
                        {
                            nodes.Add(new Node { t = t, actions = new List<Action> { action }, Fluents = possibility });
                        }
                    }
                }
            }

            foreach (var action in list)
            {
                for (var i = 0; i < nodes.Count(); i++)
                {
                    if (!nodes[i].actions.Any(n => n.IsConflictingWith(action)))
                    {
                        action.Prepare(nodes[i].Fluents);
                    }
                }
            }
            node.Nodes = nodes;
        }

        public Node Run(Scenario sc)
        {
            root = new Node { Nodes = new List<Node>(), Fluents = sc._initialFluents };

            Build(root, sc._actions);

            return root;
        }

        public override String ToString()
        {
            var sb2 = new StringBuilder();
            Stringify("", root, sb2);
            return sb2.ToString();
        }

        private void Stringify(String str, Node node, StringBuilder sb2)
        {
            str += "\n T" + node.t + ", Actions:";
            str = node.actions.Aggregate(str, (current, action) => current + (action.Name + ", "));
            str += " Fluents:";
            str = node.Fluents.Aggregate(str, (current, fluent) => current + (fluent.Name + " is " + (fluent.Value.HasValue ? fluent.Value.Value.ToString() : "?") + ", "));
            if (node.Nodes == null || !node.Nodes.Any())
                sb2.Append(str + "\n");
            else
            {
                foreach (var n in node.Nodes)
                {
                    Stringify(str, n, sb2);
                }
            }
        }
    }
}
